package main

import (
	"fmt"
	"reflect"
)

/*
  reflect.Value是通过reflect.ValueOf(X)获得的，只有当X是指针的时候，才可以通过reflec.Value修改实际变量X的值，
  即：要修改反射类型的对象就一定要保证其值是“addressable”的。

  要想修改一个变量的值，那么必须通过该变量的指针地址 , 取消指针的引用 。
  通过refPtrVal := reflect.Valueof( &var )的方式获取指针类型，
  使用refPtrVal.elem( ).set（一个新的reflect.Value）来进行更改，传递给set()的值也必须是一个reflect.value。

  func (v Value) Elem() Value
  Elem返回接口v包含的值或指针v指向的值。如果v的类型不是interface或ptr，它会恐慌。如果v为零，则返回零值。

  变量是一个指针、map、slice、channel、Array。那么你可以使用reflect.Typeof(v).Elem()来确定包含的类型。
 */

type Student struct {
	Name string
	Age  int
	School string
}

func main() {

	/*
		1. 需要传入的参数是* float64这个指针，然后可以通过pointer.Elem()去获取所指向的Value，注意一定要是指针。
		2. 如果传入的参数不是指针，而是变量，那么
			通过Elem获取原始值对应的对象则直接panic
			通过CanSet方法查询是否可以设置返回false
		3. newValue.CantSet()表示是否可以重新设置其值，如果输出的是true则可修改，否则不能修改，修改完之后再进行打印发现真的已经修改了。
		4. reflect.Value.Elem() 表示获取原始值对应的反射对象，只有原始对象才能修改，当前反射对象是不能修改的
		5. 也就是说如果要修改反射类型对象，其值必须是“addressable”【对应的要传入的是指针，同时要通过Elem方法获取原始值对应的反射对象】
		6. struct 或者 struct 的嵌套都是一样的判断处理方式
	*/
	var num float64 = 1.2345

	//需要操作指针
	//通过reflect.ValueOf获取num中的reflect.Value，注意，参数必须是指针才能修改其值
	ptr := reflect.ValueOf(&num)
	newValue := ptr.Elem()

	fmt.Println("类型 :", newValue.Type()) //float64
	fmt.Println("是否可以修改:", newValue.CanSet())

	// 重新赋值
	newValue.SetFloat(77.89)
	fmt.Println("new value is : ", num)

	stu :=Student{"zhangsan", 18, "school"}

	fmt.Printf("%T \n", stu)

	stuPtr := &stu
	fmt.Printf("%T\n",stuPtr)

	fmt.Println(stu.Name)
	fmt.Println(stuPtr.Name)

	val := reflect.ValueOf(&stu)
	if val.Kind() == reflect.Ptr {
		fmt.Println(val.Elem().CanSet())
		val = val.Elem()
	}
	name := val.FieldByName("Name")
	name.SetString("lisi")

	school := val.FieldByName("School")
	school.SetString("school01")

	fmt.Println("new stu values : ", stu)

}
